﻿using LeRenard.ScriptWars.Model.Entities;
using System;
using System.Collections.Generic;
namespace LeRenard.ScriptWars.Model.Scripting.Definitions.Fluent
{
    /// <summary>
    /// Defines an object that "builds" a <see cref="OperationDefinition"/>.
    /// </summary>
    public class OperationDefinitionBuilder
    {
        #region Properties & Fields
        /// <summary>
        /// Holds the <see cref="IOperationDefinition"/>'s name.
        /// </summary>
        private readonly string _operationName;

        /// <summary>
        /// Holds the parameters.
        /// </summary>
        private readonly IDictionary<int, IOperationParameterDefinition> _parameters;

        /// <summary>
        /// Holds the execution method.
        /// </summary>
        private Action<IEntity, IAlgorithmCycler, IGame, IOperation> _executionMethod;
        #endregion

        #region Construction
        /// <summary>
        /// Creates a new <see cref="OperationDefinitionBuilder"/> object.
        /// </summary>
        /// <param name="name">The name the <see cref="IOperationDefinition"/> will have.</param>
        internal OperationDefinitionBuilder(string name)
        {
            ThrowIf.NullOrEmpty(() => name);

            _operationName = name;
            _parameters = new Dictionary<int, IOperationParameterDefinition>();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Adds an <see cref="IOperationParameterDefinition"/>.
        /// </summary>
        /// <param name="name">Name of the parameter.</param>
        /// <param name="type">Type of the parameter.</param>        
        public OperationDefinitionBuilder AddParameter(string name, OperationParameterType type)
        {
            ThrowIf.NullOrEmpty(() => name);

            foreach (var parameter in _parameters.Values) {
                if (parameter.Name.Equals(name.Trim().ToLower())) {
                    throw new ArgumentException("Parameter with name '{0}' already exists.".FormatWith(name), "name");
                }
            }

            var parameterCount = _parameters.Count;
            var parameterDefinition = new OperationParameterDefinition(name, type);
            _parameters.Add(parameterCount + 1, parameterDefinition);

            return this;
        }

        /// <summary>
        /// Adss the execution method. Essentially the algorithm to execute.
        /// </summary>        
        public OperationDefinitionBuilder AddExecutionMethod(Action<IEntity, IAlgorithmCycler, IGame, IOperation> executionMethod)
        {
            ThrowIf.Null(() => executionMethod);

            _executionMethod = executionMethod;

            return this;
        }

        /// <summary>
        /// Builds the <see cref="IOperationDefinition"/> with the given options.
        /// Ends the fluent construction process.
        /// </summary>        
        public IOperationDefinition Build()
        {
            return new OperationDefinition(_operationName, _executionMethod, _parameters);
        }
        #endregion
    }
}
